<?php

namespace App\Service\Cruise\CruiseTemplate;

use App\Enums\BaseErrorCode;
use App\Exceptions\Custom\ResponseHttpException;
use App\Models\Cruise\CruiseCheckSelfTaskModel;
use App\Models\Cruise\CruiseTaskProjectModel;
use App\Models\Cruise\CruiseTemplateModel;
use App\Models\Cruise\CruiseTemplatePositionModel;
use App\Models\Cruise\CruiseTemplatePositionProjectMappingModel;
use App\Remote\Rms\RmsUserRemote;
use App\Service\BaseService;
use App\Service\Cruise\CruiseProject\CruiseBaseService;
use App\Support\CustomForm\AttributeCruiseSupport;
use App\Support\CustomForm\AttributeSupport;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class CruiseTemplateService
{
    private int $userId     = 0;
    private int $templateId = 0;

    /**
     * @param int $userId
     */
    public function setUserId(int $userId): self
    {
        $this->userId = $userId;
        return $this;
    }

    /**
     * @param int $templateId
     */
    public function setTemplateId(int $templateId): self
    {
        $this->templateId = $templateId;
        return $this;
    }

    private function getTemplateModel(): CruiseTemplateModel
    {
        $template = CruiseTemplateModel::query()->find($this->templateId);
        if (!$template) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, "不存在的templateId{$this->templateId}");
        }

        return $template;
    }

    /**
     * 获取单例
     * @return static
     */
    public static function getIns(): self
    {
        $cacheKey = 'CruiseTemplateServiceIns';
        $ins = Cache::store('array')->get($cacheKey);
        if ($ins) {
            return $ins;
        }

        $ins = new self();
        Cache::store('array')->set($cacheKey, $ins, 1000);

        return $ins;
    }

    /**
     * 系统模板校验
     * @param int $status
     * @param int $sysTemplate
     * @return void
     */
    private function checkSysTemplate(int $status, int $sysTemplate)
    {
        if (CruiseTemplateModel::STATUS_ON == $status && in_array($sysTemplate, CruiseTemplateModel::TEMPLATE_SINGLE)) {
            $ct = CruiseTemplateModel::query()
                                     ->where('sysTemplate', $sysTemplate)
                                     ->where('status', CruiseTemplateModel::STATUS_ON)
                                     ->where('id', '!=', $this->templateId)
                                     ->count();
            if ($ct) {
                throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null,
                    "不允许启用多个" . CruiseTemplateModel::SYS_TEMPLATE_TEXT[$sysTemplate] . "模板");
            }
        }
    }

    /**
     * 新增/更新模板
     * @param array $templateBase
     * @param array $positions
     * @return int
     */
    public function addOrEdit(array $templateBase, array $positions): int
    {
        // model校验
        if ($this->templateId) {

            // template
            $this->getTemplateModel();
        } else {
            $this->templateId = (int)CruiseBaseService::popId(new CruiseTemplateModel());
        }

        // 系统模板校验
        $this->checkSysTemplate($templateBase['status'], $templateBase['sysTemplate']);

        // projectId校验
        $projectIds = collect();
        collect($positions)->pluck('project')->each(function ($item) use (&$projectIds) {
            $projectIds = $projectIds->merge(collect($item)->pluck('projectId'))->filter()->unique()->values();
        });

        $projectCount = CruiseTaskProjectModel::query()->whereIn('id', $projectIds)->count();
        if ($projectCount != $projectIds->count()) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, "请检查项目id，不能添加不存在的id");
        }

        // projectId select校验
        $projectIds = collect();
        collect($positions)->pluck('project')->each(function ($item) use (&$projectIds) {
            $projectIds = $projectIds->merge(collect($item)->pluck('relations.projectId'))
                                     ->filter()
                                     ->unique()
                                     ->values();
        });

        $projectCount = CruiseTaskProjectModel::query()
                                              ->whereIn('id', $projectIds)
                                              ->whereIn('inputType', [
                                                  AttributeSupport::INPUT_TYPE_SELECT,
                                                  AttributeSupport::INPUT_TYPE_SELECT_MULTIPLE,
                                                  AttributeCruiseSupport::INPUT_TYPE_UPDATE_KF_SYSTEM,
                                              ])
                                              ->count();
        if ($projectCount != $projectIds->count()) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null, "请检查项目id，不能添加不存在的id。或使用了新组件");
        }

        // project组件关系校验
        $this->moduleRelationCheck($positions);

        DB::beginTransaction();

        // template
        $this->templateId = CruiseTemplateModel::query()->updateOrCreate(['id' => $this->templateId], [
            'name'               => $templateBase['name'],
            'desc'               => $templateBase['desc'],
            'type'               => $templateBase['type'],
            'sysTemplate'        => $templateBase['sysTemplate'],
            'status'             => $templateBase['status'],
            'antiCheatingSwitch' => $templateBase['antiCheatingSwitch'] ?? CruiseTemplateModel::ANTI_CHEATING_SWITCH_OFF
        ])->id;

        // 更新前先删除
        if ($this->templateId) {
            CruiseTemplatePositionModel::query()->where('templateId', $this->templateId)->delete();
        }

        foreach ($positions as $position) {

            $positionId = $position['positionId'] ?? 0;
            if (!$positionId) {
                $positionId = (int)CruiseBaseService::popId(new CruiseTemplatePositionModel);
            }

            // position
            $positionId = CruiseTemplatePositionModel::query()->create([
                'id'         => $positionId,
                'name'       => $position['positionName'],
                'templateId' => $this->templateId
            ])->id;

            // 更新前先删除
            if ($this->templateId) {
                CruiseTemplatePositionProjectMappingModel::query()
                                                         ->where('templateId', $this->templateId)
                                                         ->whereIn('positionId', $position)
                                                         ->delete();
            }

            // projects
            $mappings = [];
            foreach ($position['project'] as $project) {

                $mappings[] = [
                    'templateId'        => $this->templateId,
                    'positionId'        => $positionId,
                    'projectId'         => $project['projectId'],
                    'relationProjectId' => $project['relations']['projectId'] ?? 0,
                    'relationOptions'   => json_encode($project['relations']['optionsValue'] ?? []),
                    'createdAt'         => time(),
                    'updatedAt'         => time(),
                    'createdByUid'      => $this->userId,
                    'updatedByUid'      => $this->userId,
                ];

            }

            // project-mappings
            CruiseTemplatePositionProjectMappingModel::query()->insert($mappings);
        }

        DB::commit();

        return $this->templateId;
    }

    /**
     * 模板列表
     * @param string $name
     * @param int $pageNum
     * @param int $pageSize
     * @return array
     */
    public function lists(string $name, int $pageNum, int $pageSize, array $types, string $status): array
    {
        $paginator = CruiseTemplateModel::query()
                                        ->when($name, function ($query) use ($name) {
                                            $query->where('name', 'like', "%{$name}%");
                                        })
                                        ->when(strlen($status) && in_array($status, array_keys(CruiseTemplateModel::STATUS_TEXT)),
                                            function ($query) use ($status) {
                                                $query->where("status", $status);
                                            })
                                        ->whereIn('type', $types)
                                        ->orderByDesc('id')
                                        ->paginate($pageSize, ['*'], 'pageNum', $pageNum);
        $userIds   = $paginator->pluck('updatedByUid')->filter()->toArray();
        if ($userIds) {
            $userInfos = collect(RmsUserRemote::getRmsUserByIds($userIds, ['id', 'username']))->keyBy('id');
        } else {
            $userInfos = collect();
        }

        return [
            'count'    => $paginator->total(),
            'list'     => $paginator->map(function ($item) use ($userInfos) {
                return [
                    'id'                     => $item->id,
                    'name'                   => $item->name,
                    'type'                   => $item->type,
                    'typeText'               => $item->typeText,
                    'sysTemplate'            => $item->sysTemplate,
                    'sysTemplateText'        => $item->sysTemplateText,
                    'antiCheatingSwitch'     => $item->antiCheatingSwitch,
                    'antiCheatingSwitchText' => CruiseTemplateModel::ANTI_CHEATING_SWITCH_TEXT[$item->antiCheatingSwitch] ?? "",
                    'updatedName'            => $userInfos[$item->updatedByUid]['username'] ?? '',
                    'updatedAt'              => $item->updatedAt->toDateString(),
                    'status'                 => $item->status,
                    'statusText'             => $item->status == CruiseTemplateModel::STATUS_ON ? '开启' : '关闭',
                ];
            }),
            'pageNum'  => $pageNum,
            'pageSize' => $pageSize,
        ];
    }

    /**
     * 详情
     * @param int $id
     * @return array
     */
    public function detail(): array
    {
        $template = $this->getTemplateModel();

        $data = [
            'template' => [
                'id'                 => $this->templateId,
                'name'               => $template->name,
                'desc'               => $template->desc,
                'type'               => $template->type,
                'sysTemplate'        => $template->sysTemplate,
                'status'             => $template->status,
                'antiCheatingSwitch' => $template->antiCheatingSwitch,
            ],
            'positions' => $template->positionsRel->map(function ($position) {
                return [
                    'positionId' => $position->id,
                    'name'       => $position->name,
                    'project'    => $position->positionProjectMappingsRel->map(function ($mapping) {

                        $item = $this->projectStruct($mapping->projectRel);
                        if (!$item) {
                            return null;
                        }

                        $item['relations'] = [
                            'projectId'       => $mapping->relationProjectId,
                            'optionsValue'    => json_decode($mapping->relationOptions, true),
                            'relationProject' => $this->projectStruct($mapping->relationProjectRel),
                        ];

                        return $item;
                    })->filter()->values(),
                ];
            }),
        ];

        return $data;
    }

    /**
     * 没有位置的projects
     * @param int $templateId
     * @return array
     */
    public function noPositionProjects(): array
    {
        return CruiseTemplatePositionProjectMappingModel::query()->where('templateId', $this->templateId)->get()->map(function ($mapping) {
            $item = $this->projectStruct($mapping->projectRel);
            if (!$item) {
                return null;
            }

            $item['relations'] = [
                'projectId'       => $mapping->relationProjectId,
                'optionsValue'    => json_decode($mapping->relationOptions, true),
                'relationProject' => $this->projectStruct($mapping->relationProjectRel),
            ];

            return $item;
        })->filter()->values()->toArray();
    }

    /**
     * 上下架
     * @param int $templateId
     * @param int $status
     * @return int
     */
    public function offline(int $status): int
    {
        $template = $this->getTemplateModel();

        // 系统模板校验
        $this->checkSysTemplate($status, $template['sysTemplate']);

        $template->status = $status;

        return $template->save();
    }

    private function projectStruct(?CruiseTaskProjectModel $projectModel): array
    {
        if (!$projectModel) {
            return [];
        }

        return [
            'projectId'     => $projectModel->id,
            'name'          => $projectModel->name,
            'inputType'     => $projectModel->inputType,
            'inputTypeText' => $projectModel->inputTypeText,
            'options'       => $projectModel->optionsArr,
            'auditValue'    => $projectModel->auditValue,
        ];
    }

    public static function getTemplateId(int $type): int
    {
        $template = CruiseTemplateModel::query()
                                       ->where('type', $type)
                                       ->where('status', CruiseTemplateModel::STATUS_ON)
                                       ->orderBy('id')
                                       ->first();

        if (!$template) {
            throw new ResponseHttpException(BaseErrorCode::$ERROR_OCCURRED, null,
                "获取巡店模板失败:{$type}");
        }

        return $template->id;
    }

    /**
     * 二级模板列表
     * @return array
     */
    public static function templateTypeLists(array $types): array
    {
        return collect(CruiseTemplateModel::TEMPLATE_TYPES)->map(function ($lists, $template1) use ($types) {

            if (!in_array($template1, $types)) {
                return null;
            }

            return [
                'text'  => CruiseTemplateModel::TYPE_TEXT[$template1],
                'value' => $template1,
                'lists' => collect($lists)->map(function ($template2) {
                    return [
                        'text'  => CruiseTemplateModel::SYS_TEMPLATE_TEXT[$template2] ?? '',
                        'value' => $template2,
                    ];
                })->toArray(),
            ];
        })->filter()->values()->toArray();
    }

    /**
     * 通过sysTemplate获取模板type
     * @param int $sysTemplate
     * @return int
     */
    public static function getTemplateTypeBySysTemplate(int $sysTemplate): int
    {
        foreach (CruiseTemplateModel::TEMPLATE_TYPES as $type => $lists) {
            if (in_array($sysTemplate, $lists)) {
                return $type;
            }
        }

        return 0;
    }

    /**
     * 根据模板类型 & 规则获取模板列表
     * @param int $type
     * @return array
     */
    public static function selfCheckTemplateList(int $type, int $rule): array
    {
        if ($type == CruiseTemplateModel::TYPE_5) {
            $sysTemplate = [CruiseTemplateModel::SYS_TEMPLATE_FACTORY_SELF];
        } elseif ($type == CruiseTemplateModel::TYPE_8) {

            switch ($rule) {
                case CruiseCheckSelfTaskModel::RULE_DAY:
                    $sysTemplate = [
                        CruiseTemplateModel::SYS_TEMPLATE_SELF_OPEN,
                        CruiseTemplateModel::SYS_TEMPLATE_SELF_CLOSE
                    ];
                    break;
                case CruiseCheckSelfTaskModel::RULE_WEEK:
                    $sysTemplate = [CruiseTemplateModel::SYS_TEMPLATE_SELF_WEEK];
                    break;
                case CruiseCheckSelfTaskModel::RULE_MONTH:
                    $sysTemplate = [CruiseTemplateModel::SYS_TEMPLATE_SELF_MONTH];
                    break;
                default:
                    $sysTemplate = [-1];
                    break;
            }
        } else {
            $sysTemplate = CruiseTemplateModel::TEMPLATE_TYPES[$type] ?? [];
        }

        return [
            "list" => CruiseTemplateModel::query()
                                         ->where("type", $type)
                                         ->whereIn("sysTemplate", $sysTemplate)
                                         ->where("status", CruiseTemplateModel::STATUS_ON)
                                         ->select(["id", "name", "desc", "type", "sysTemplate"])
                                         ->get()
        ];
    }

    /**
     * 模块关系校验
     * @param array $positions
     * @return void
     */
    private function moduleRelationCheck(array $positions): void
    {
        $projectIds = [];
        foreach ($positions as $position) {
            foreach ($position['project'] as $project) {
                $projectIds[] = $project['projectId'];
            }
        }

        $projects = CruiseTaskProjectModel::query()->whereIn('id', $projectIds)->select('inputType')->distinct()->get();

        // 1. 巡检项不能同时出现在一个模板中
        $cruises = $projects->whereIn('inputType', AttributeCruiseSupport::CRUISE_CHECK_MODULE);

        if ($cruises->count() > 1) {
            BaseService::throwErr('不同类型的巡检项不能同时出现在一个模板中');
        }
    }
}
